package com.sl.core.engine.exchange.rest;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.hutool.json.JSONUtil;
import com.bazaarvoice.jolt.Chainr;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.collect.Maps;
import com.sl.core.domain.dto.JsonMappingFieldDefine;
import com.sl.core.engine.exchange.ApiCallBackService;
import com.sl.core.engine.transfer.MappingFiledDefineToJoltDslTransfer;
import com.sl.core.engine.utils.TidUtil;
import com.sl.core.utils.JsonUtil;
import jakarta.ws.rs.core.MediaType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.camel.Exchange;
import org.apache.camel.Message;
import org.apache.camel.Processor;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.http.common.HttpMessage;
import org.apache.camel.model.rest.RestBindingMode;
import org.apache.camel.model.rest.RestDefinition;
import org.apache.commons.compress.utils.Lists;
import org.springframework.core.env.Environment;

import java.util.Collections;
import java.util.List;
import java.util.Map;

@Slf4j
@RequiredArgsConstructor
public class RestApi extends RouteBuilder {

    private final ApiCallBackService apiCallBackService;

    private final String baseRouteId;




//http://127.0.0.1:8081/square-lake/kapi/atc/ss?sdfdf=ff&sdfdf=dfdf

    @Override
    public void configure() throws Exception {
//        getCamelContext().removeRoute()
        List<ApiConfigDTO> restApiDTOS = apiCallBackService.publishByQueryList(baseRouteId);

        if (CollUtil.isEmpty(restApiDTOS)) {
            return;
        }

        Environment env = SpringUtil.getBean(Environment.class);
        String serverPort = env.getProperty("server.port");
        onException(Exception.class).handled(true).process(new Processor() {
            @Override
            public void process(Exchange exchange) throws Exception {
                Throwable cause = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Throwable.class);
                exchange.getMessage().setHeader(Exchange.HTTP_RESPONSE_CODE, 500);
                exchange.getMessage().setHeader(Exchange.CONTENT_TYPE, MediaType.APPLICATION_JSON);
                exchange.getMessage().setBody("{error:" + cause.getMessage() + "}");
            }
        });
        //从数据库里面读取
        restConfiguration()
//                .contextPath("/kapi")
//                .apiContextPath("/api-doc")
//                .apiProperty("api.title", "REST API for processing Order")
//                .apiProperty("api.version", "1.0")
//                .apiProperty("cors", "true")
//                .apiContextRouteId("doc-api")
                .component("servlet")
                .bindingMode(RestBindingMode.auto)
                .dataFormatProperty("prettyPrint", "true")

//                .dataFormatProperty("json.in.disableFeatures", "FAIL_ON_UNKNOWN_PROPERTIES,ADJUST_DATES_TO_CONTEXT_TIME_ZONE")
//                .dataFormatProperty("json.in.enableFeatures", "FAIL_ON_NUMBERS_FOR_ENUMS,USE_BIG_DECIMAL_FOR_FLOATS")
        ;
        ProcessProcessor processProcessor = new ProcessProcessor(apiCallBackService);

        for (ApiConfigDTO restApiDTO : restApiDTOS) {

            String contextPath = restApiDTO.getContextPath();

            String method = restApiDTO.getMethods();
            if (StrUtil.isBlank(method)) {
                method = "post";
            }
            List<String> methodList = Lists.newArrayList();
            if (JSONUtil.isTypeJSON(method)) {
                methodList = JSONUtil.toList(method, String.class);
            } else {
                methodList.add(method);
            }

            String path = restApiDTO.getPath();

            for (String s : methodList) {
                if (StrUtil.equalsAnyIgnoreCase(s, "option")) {
                    continue;
                }
                RestDefinition rest = null;
                if (StrUtil.isNotBlank(contextPath)) {
                    rest = rest(contextPath);
                } else {
                    rest = rest();
                }
                if (StrUtil.equalsAnyIgnoreCase(s, "get")) {
                    rest.get(path);
                } else if (StrUtil.equalsAnyIgnoreCase(s, "post")) {
                    rest.post(path);
                } else if (StrUtil.equalsAnyIgnoreCase(s, "delete")) {
                    rest.delete(path);
                } else if (StrUtil.equalsAnyIgnoreCase(s, "put")) {
                    rest.put(path);
                }
                rest.enableCORS(true);
                rest.routeId(s.toLowerCase() + "_" + restApiDTO.getId());
                rest.apiDocs(true);
                rest.setId(restApiDTO.getId());
                rest.description(restApiDTO.getDescription());
                rest.to("direct:process");


                //
                //http://127.0.0.1:8081/square-lake/kapi/atc/ss?sdfdf=ff&sdfdf=dfdf
                log.info("publish openapi {}", s + " http://127.0.0.1:"+serverPort+"/kapi" + (StrUtil.isNotBlank(contextPath) ? contextPath : "") + path);

            }

        }
        from("direct:process").process(processProcessor);

//                .type(AdderRequest.class)    // 注2
//                .outType(AdderResponse.class)    // 注3
//                .produces("application/json")    // 注4
//                .consumes("application/json")
//                .pro;    // 注5


//        from("netty-http:http://localhost:8088/foo")
//                .transform().constant("Bye World.");    // 注6

    }

    @RequiredArgsConstructor
    public static class ProcessProcessor implements Processor {

        private final ApiCallBackService apiCallBackService;


        @Override
        public void process(Exchange exchange) throws Exception {
            HttpMessage message = exchange.getMessage(HttpMessage.class);
            //requestBody
            Object body = message.getBody();
            Message output = exchange.getMessage();
            //header参数
            Map<String, Object> headers = exchange.getIn().getHeaders();

            //form
            String fromRouteId = exchange.getFromRouteId();

            List<String> split = StrUtil.split(fromRouteId, "_");


            if (CollUtil.isEmpty(split)) {
                Map<String, Object> result = buildDefaultResponse(null, "service not found", 400);
                output.setBody(result);
                return;
            }
//            JsonPathExpression jsonPathExpression = new JsonPathExpression("$.data.orgId");
//            jsonPathExpression.setResultType(String.class);

            String apiConfigId = CollUtil.get(split, 1);
            String method = CollUtil.get(split, 0);
            //找到路由信息
            ApiConfigDTO restApiDTO = apiCallBackService.queryById(apiConfigId);

            if (ObjectUtil.isNull(restApiDTO)) {
                Map<String, Object> result = buildDefaultResponse(null, "service not found", 400);
                output.setBody(result);
            }

            String apiStatusCode = restApiDTO.getApiStatusCode();
            if (!StrUtil.equals(apiStatusCode, "ONLINE")) {
                Map<String, Object> result = buildDefaultResponse(null, "service is offline", 401);
                output.setBody(result);
                return;
            }

            //找到latest版本
            List<ApiVersionConfigDTO> versionList = restApiDTO.getVersionList();
            ApiVersionConfigDTO apiVersionConfigDTO = null;
            if (CollUtil.size(versionList) == 1) {
                apiVersionConfigDTO = CollUtil.get(versionList, 0);
            } else {
                apiVersionConfigDTO = versionList.stream().filter(v -> BooleanUtil.isTrue(v.getLatestFlag())).findFirst().orElse(null);
            }

            if (ObjectUtil.isNull(apiVersionConfigDTO)) {
                Map<String, Object> result = buildDefaultResponse(null, "service version is not found", 402);
                output.setBody(result);
                return;
            }

            //header校验
            List<ApiHeaderConfigDTO> headerConfig = apiVersionConfigDTO.getHeaders();
            List<String> strings = headerValidate(headers, headerConfig);
            if (CollUtil.isNotEmpty(strings)) {
                Map<String, Object> result = buildDefaultResponse(null, String.join(",", strings), 403);
                output.setBody(result);
                return;
            }

            ApiEntityConfigDTO entity = apiVersionConfigDTO.getEntity();

//            private final ProcessRelationConfigBizService relationConfigBizService;
            String tid = TidUtil.getTid(exchange.getExchangeId());
            Object run = null;
            try {
                run = apiCallBackService.run(apiConfigId, tid, body);
            } catch (Exception e) {
                Map<String, Object> result = buildDefaultResponse(null, ExceptionUtil.getMessage(e), 500);
                output.setBody(result);
                return;
            }

            //需要包裹不
            Boolean resultWrapperFlag = restApiDTO.getResultWrapperFlag();
            String resultWrapperConfig = restApiDTO.getResultWrapperConfig();
            if (BooleanUtil.isFalse(resultWrapperFlag)) {
                output.setBody(run);
            } else {
                Map<String, Object> map = buildDefaultResponse(run, null, 200);
                if (StrUtil.isBlank(resultWrapperConfig)) {
                    output.setBody(map);
                    return;
                }
                Map<String, Object> resultMapConfig = JsonUtil.toMap(resultWrapperConfig, String.class, Object.class);
                Map<String, Object> targetFieldDefine = MapUtil.get(resultMapConfig, "targetFieldDefine", Map.class);
                if (MapUtil.isEmpty(targetFieldDefine)) {
                    output.setBody(map);
                    return;
                }
//                JsonFieldDefine bean = JsonUtil.toBean(targetFieldDefine, JsonFieldDefine.class);
                List<Map<String, Object>> params = MapUtil.get(targetFieldDefine, "params", List.class);
                //找到对象关系
                List<JsonMappingFieldDefine> jsonMappingFieldDefine = JsonUtil.toBean(params, new TypeReference<List<JsonMappingFieldDefine>>() {
                });

                if (CollUtil.isEmpty(jsonMappingFieldDefine)) {
                    output.setBody(map);
                    return;
                }

                //转移到了映射
                String jolt = MappingFiledDefineToJoltDslTransfer.transfer(jsonMappingFieldDefine);

                List chainrSpecJSON = JsonUtil.toList(jolt, Map.class);
                Chainr chainr = Chainr.fromSpec(chainrSpecJSON);
                Object transformedOutput = chainr.transform(map);
                output.setBody(transformedOutput);
            }
        }


        private Map<String, Object> buildDefaultResponse(Object data, String errorMsg, Integer code) {
            if (ObjectUtil.isNull(code)) {
                code = 200;
            }
            Map<String, Object> map = Maps.newHashMap();
            map.put("code", code);
            map.put("msg", errorMsg);
            map.put("data", data);

            return map;
        }


        private List<String> headerValidate(Map<String, Object> headers, List<ApiHeaderConfigDTO> headerConfig) {

            if (CollUtil.isEmpty(headerConfig)) {
                return Collections.emptyList();
            }
            List<String> result = Lists.newArrayList();

            for (ApiHeaderConfigDTO apiHeaderConfigDTO : headerConfig) {
                Boolean requiredFlag = apiHeaderConfigDTO.getRequiredFlag();
                String headerName = apiHeaderConfigDTO.getHeaderName();

                Object o = headers.get(headerName);

                if (ObjectUtil.isNull(o) && BooleanUtil.isTrue(requiredFlag)) {
                    result.add("header中" + headerName + "必填");
                }
            }

            return result;
        }
    }


}
